using System;
using System.IO;

namespace System.Windows.Media.DirectShow.Ogg
{

    public class OggPage
    {

        private int version;
        private bool continued, bos, eos;
        private long absoluteGranulePosition;
        private int streamSerialNumber, pageSequenceNumber, pageCheckSum;
        private int[] segmentOffsets;
        private int[] segmentLengths;
        private int totalLength;
        private byte[] data;

        private OggPage(
           int version,
           bool continued,
           bool bos,
           bool eos,
           long absoluteGranulePosition,
           int streamSerialNumber,
           int pageSequenceNumber,
           int pageCheckSum,
           int[] segmentOffsets,
           int[] segmentLengths,
           int totalLength,
           byte[] data)
        {

            this.version = version;
            this.continued = continued;
            this.bos = bos;
            this.eos = eos;
            this.absoluteGranulePosition = absoluteGranulePosition;
            this.streamSerialNumber = streamSerialNumber;
            this.pageSequenceNumber = pageSequenceNumber;
            this.pageCheckSum = pageCheckSum;
            this.segmentOffsets = segmentOffsets;
            this.segmentLengths = segmentLengths;
            this.totalLength = totalLength;
            this.data = data;
        }

        public static OggPage Create(Stream source)
        {
            return Create(new BinaryReader(source), false);
        }

        public static OggPage Create(BinaryReader source)
        {
            return Create(source, false);
        }

        public static OggPage Create(Stream source, bool skipData)
        {
            return Create(new BinaryReader(source), skipData);
        }

        public static OggPage Create(BinaryReader source, bool skipData)
        {
            if (source.BaseStream.Position > source.BaseStream.Length - 4)
                return null;

            int capture = source.ReadInt32();

            if (capture != 0x5367674f)
            {
                throw new OggFormatException("Ogg page does not start with 'OggS' (0x4f676753)");
            }

            int version = source.ReadByte();
            byte tmp = source.ReadByte();
            bool bf1 = (tmp & 1) != 0;
            bool bos = (tmp & 2) != 0;
            bool eos = (tmp & 4) != 0;
            long absoluteGranulePosition = source.ReadInt64();
            int streamSerialNumber = source.ReadInt32();
            int pageSequenceNumber = source.ReadInt32();
            int pageCheckSum = source.ReadInt32();
            int pageSegments = source.ReadByte();

            int[] segmentOffsets = new int[pageSegments];
            int[] segmentLengths = new int[pageSegments];
            int totalLength = 0;

            for (int i = 0; i < pageSegments; i++)
            {
                int l = (int)source.ReadByte();
                segmentLengths[i] = l;
                segmentOffsets[i] = totalLength;
                totalLength += l;
            }

            byte[] data = null;

            if (!skipData)
            {
                data = new byte[totalLength];
                source.Read(data, 0, (int)totalLength);
            }

            return new OggPage(version, bf1, bos, eos, absoluteGranulePosition, streamSerialNumber, pageSequenceNumber, pageCheckSum, segmentOffsets, segmentLengths, totalLength, data);

        }

        public long AbsoluteGranulePosition
        {
            get
            {
                return absoluteGranulePosition;
            }
        }

        /**
         * Returns the stream serial number of this ogg page.
         *
         * @return this page's serial number
         */

        public int StreamSerialNumber
        {
            get
            {
                return streamSerialNumber;
            }
        }

        /**
         * Return the sequnce number of this ogg page.
         *
         * @return this page's sequence number
         */

        public long PageSequenceNumber
        {
            get
            {
                return pageSequenceNumber;
            }
        }

        /**
         * Return the check sum of this ogg page.
         *
         * @return this page's check sum
         */

        public long PageCheckSum
        {
            get
            {
                return pageCheckSum;
            }
        }

        /**
         * Return the number of data segment in this ogg page.
         *
         * @return this page's number of data segments
         */

        public long PageSegmentCount
        {
            get
            {
                return segmentOffsets.Length;
            }
        }

        public int[] SegmentOffsets
        {
            get
            {
                return segmentOffsets;
            }
        }

        public int[] SegmentLengths
        {
            get
            {
                return segmentLengths;
            }
        }

        /**
         * @return the total number of bytes in the page data
         */


        public long TotalLength
        {
            get
            {
                return totalLength;
            }
        }

        /**
         * @return a ByteBuffer containing the page data
         */


        //public byte[] getData() {
        //   return data;
        //}

        /**
         * @return <code>true</code> if this page begins with a continued packet
         */

        public bool Continued
        {
            get
            {
                return continued;
            }
        }

        /**
         * @return <code>true</code> if this page begins with a fresh packet
         */

        public bool Fresh
        {
            get
            {
                return !continued;
            }
        }


        /**
         * @return <code>true</code> if this page is the beginning of a logical stream
         */

        public bool BOS
        {
            get
            {
                return bos;
            }
        }

        /**
         * @return <code>true</code> if this page is the end of a logical stream
         */

        public bool EOS
        {
            get
            {
                return eos;
            }
        }

        public byte[] Data
        {
            get
            {
                return data;
            }
        }
    }

}